﻿using System;
using System.Linq.Expressions;

////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Dimok.Expressions
//
// summary:	.
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Dimok.Expressions {

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Expression extensions. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public static class ExpressionExtensions {

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	An Expression<Func<TViewModel>> extension method that gets a property name. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <typeparam name="TViewModel">	Type of the view model. </typeparam>
		/// <param name="property">	The property to act on. </param>
		///
		/// <returns>	The property name< t view model> </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string GetPropertyName<TViewModel>(this Expression<Func<TViewModel>> property) {
			var expression = property.Body as MemberExpression;
			return expression.Member.Name;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	An Expression<Func<object>> extension method that gets a property name. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="propertyLamda">	The propertyLamda to act on. </param>
		///
		/// <returns>	The property name. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		static string GetPropertyName(this Expression<Func<object>> propertyLamda) {
			var body = propertyLamda.Body as UnaryExpression;
			if (body == null)
				return (propertyLamda as LambdaExpression).GetPropertyName();
			else {
				var operand = body.Operand as MemberExpression;
				var member = operand.Member;
				return member.Name;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	An Expression<Func<object>> extension method that gets a property name. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="ArgumentException">	Thrown when one or more arguments have unsupported or
		/// 																			illegal values. </exception>
		///
		/// <param name="propertyExpression">	The propertyExpression to act on. </param>
		///
		/// <returns>	The property name. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		static string GetPropertyName(this LambdaExpression propertyExpression) {
			var body = propertyExpression.Body as MemberExpression;
			if (body == null)
				throw new ArgumentException("'propertyExpression' should be a member expression");
			// Extract the right part (after "=>")
			var vmExpression = body.Expression as ConstantExpression;
			if (vmExpression == null)
				throw new ArgumentException("'propertyExpression' body should be a constant expression");
			// Create a reference to the calling object to pass it as the sender
			LambdaExpression vmlambda = System.Linq.Expressions.Expression.Lambda(vmExpression);
			Delegate vmFunc = vmlambda.Compile();
			object vm = vmFunc.DynamicInvoke();
			// Extract the name of the property to raise a change on
			return body.Member.Name;
		}
	}
}
